/**
 * 
 */
package com.alstudio.event;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import android.os.Looper;

import com.alstudio.utils.log.ALLog;

/**
 * 事件管理器
 * 
 * @author alonso lee
 * 
 */
public class ALEventDeliver {

	// 缺省事件投递方法
	private String eventMethodName = "onALEvent";
	// 缺省的事件管理器
	private static volatile ALEventDeliver defaultAlEventDeliver;
	// 事件管理器工作状态回调
	private ALEventDeliverWorkingStateCallback alEventDeliverWorkingStateCallback;
	// 查找订阅对象的回调方法工具
	private final SubscriberCallbackFinder subscriberCallbackFinder;

	private final static ALEventDeliverWorkingStateObserver aLEventDeliverWorkingStateObserver = new ALEventDeliverWorkingStateObserver();

	private boolean subscribed;

	// 以抛出的事件对象为键值，保存该对象的所有回调方法
	private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<Class<?>, List<Class<?>>>();
	// 以事件订阅对象的回调方法参数做键值，保存所有事件订阅对象
	private final Map<Class<?>, CopyOnWriteArrayList<SubscriptionObject>> subscriptionObjectByEventParameterType;
	// 以订阅对象为键值，保存订阅对象的所有回调方法
	private final Map<Object, List<Class<?>>> typesBySubscriber;

	/**
	 * 创建一个缺省应用内全局使用的事件管理器
	 * 
	 * @return
	 */
	public static ALEventDeliver getDefaultAlEventDeliver() {

		if (defaultAlEventDeliver == null) {
			synchronized (ALEventDeliver.class) {
				if (defaultAlEventDeliver == null) {
					defaultAlEventDeliver = new ALEventDeliver();
					defaultAlEventDeliver.setALEventDeliverWorkingStateCallback(aLEventDeliverWorkingStateObserver);
				}
			}
		}
		return defaultAlEventDeliver;

	}

	/**
	 * 
	 */
	public ALEventDeliver() {
		// TODO Auto-generated constructor stub
		subscriberCallbackFinder = new SubscriberCallbackFinder();

		subscriptionObjectByEventParameterType = new HashMap<Class<?>, CopyOnWriteArrayList<SubscriptionObject>>();
		typesBySubscriber = new HashMap<Object, List<Class<?>>>();
		// mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(),
		// 10);
		// backgroundPoster = new BackgroundPoster(this);
		// asyncPoster = new AsyncPoster(this);
		// logSubscriberExceptions = true;

	}

	/**
	 * 设定事件管理器工作状态回调接口
	 * 
	 * @param alEventDeliverWorkingStateCallback
	 */
	public void setALEventDeliverWorkingStateCallback(
			ALEventDeliverWorkingStateCallback alEventDeliverWorkingStateCallback) {
		this.alEventDeliverWorkingStateCallback = alEventDeliverWorkingStateCallback;
	}

	/**
	 * 获取事件管理器工作状态回调接口
	 * 
	 * @return
	 */
	public ALEventDeliverWorkingStateCallback getALEventDeliverWorkingStateCallback() {
		return alEventDeliverWorkingStateCallback;
	}

	/**
	 * 设置事件投递回调方法名
	 * 
	 * @param eventMethodName
	 */
	public void setEventMethodName(String eventMethodName) {
		this.eventMethodName = eventMethodName;
	}

	/**
	 * 获取事件投递回调方法名
	 * 
	 * @return 事件投递回调方法名，缺省为‘onAlEvent’
	 */
	public String getEventMethodName() {
		return eventMethodName;
	}

	/**
	 * 注册事件订阅
	 * 
	 * @param subscriber
	 */
	public void register(Object subscriber) {
		doRegister(subscriber, eventMethodName, 0);
		alEventDeliverWorkingStateCallback.onSubscriberRegister(subscriber);
	}

	/**
	 * 解除事件订阅
	 * 
	 * @param subscriber
	 */
	public void unregister(Object subscriber) {
		// 找出该订阅对象的所有回调方法
		List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
		if (subscribedTypes != null) {
			// 根据参数类型，遍历该类的回调方法并解除
			for (Class<?> eventParameterType : subscribedTypes) {
				unubscribeByEventParameterType(subscriber, eventParameterType);
			}
			// 移除该订阅对象
			typesBySubscriber.remove(subscriber);
		} else {
			// 该订阅对象没有注册到事件管理器
			ALLog.e(subscriber.getClass() + " 并未注册到事件管理器");
		}

	}

	/**
	 * 投递指定事件到事件管理器
	 * 
	 * @param event
	 */
	public void postALEvent(Object event) {
		alEventDeliverWorkingStateCallback.onPostStart(event);
		doPostALEvent(event);

	}

	/**
	 * 检测指定的事件订阅对象是否已注册到事件管理器
	 * 
	 * @param subscriber
	 * @return
	 */
	public synchronized boolean isRegistered(Object subscriber) {
		// 这里一定要锁住
		return typesBySubscriber.containsKey(subscriber);
	}

	/**
	 * 清除缓存
	 */
	public static void clearCaches() {
		SubscriberCallbackFinder.clearCaches();
		eventTypesCache.clear();
	}

	/**
	 * 投递事件管理器回调接口
	 * 
	 * @author alonso lee
	 * 
	 */
	public interface ALEventDeliverWorkingStateCallback {

		/**
		 * 当事件订阅对象注册到事件管理器时，此回调将被执行
		 * 
		 * @param subscriber
		 */
		public void onSubscriberRegister(Object subscriber);

		/**
		 * 当事件订阅对象从事件管理器中解除注册时，此回调将被执行
		 * 
		 * @param subscriber
		 */
		public void onSubscriberUnRegister(Object subscriber);

		/**
		 * 当事件管理器开始投递事件时，此回调将被执行
		 * 
		 * @param event
		 */
		public void onPostStart(Object event);

		/**
		 * 当事件管理器向事件订阅对象投递事件是，此回调将被执行
		 * 
		 * @param subscriber
		 *            事件订阅对象
		 * @param event
		 *            待投递事件
		 */
		public void onStartPostToSubscriber(Object subscriber, Object event);

		/**
		 * 当事件管理器被终止投递事件时，此回调将被执行
		 * 
		 */
		public void onPostCancel();

		/**
		 * 当事件管理器向事件订阅对象投递完成后，此回调将被执行
		 * 
		 * @param subscriber
		 *            事件订阅对象
		 * @param event
		 *            已投递事件
		 */
		public void onPostToSubscriberCompleted(Object subscriber, Object event);

		/**
		 * 当事件管理器将指定事件完全投递后，此回调将被执行
		 * 
		 * @param event
		 */
		public void onPostCompleted(Object event);

	}

	/********************************************* 私有方法、私有类在这 ************************************************/

	private synchronized void doRegister(Object subscriber, String methodName,
			int priority) {
		// 查找订阅类的回调方法
		List<SubscriberCallback> subscriberCallback = subscriberCallbackFinder
				.findSubscriberCallback(subscriber.getClass(), methodName);
		for (SubscriberCallback cb : subscriberCallback) {
			subscribe(subscriber, cb, priority);
		}
	}

	/**
	 * 将订阅对象的回调方法注册到队列中。此方法需要掉用synchronized锁住
	 * 
	 * @param subscriber
	 *            订阅对象
	 * @param subscriberCallback
	 *            订阅对象的回调方法
	 * @param sticky
	 * @param priority
	 */
	private void subscribe(Object subscriber,
			SubscriberCallback subscriberCallback, int priority) {
		subscribed = true;
		// 回调方法的参数
		Class<?> eventParameterType = subscriberCallback.eventParameterType;
		// 用回调方法的参数作为key，查找所有订阅对象
		CopyOnWriteArrayList<SubscriptionObject> subscriptionObjects = subscriptionObjectByEventParameterType
				.get(eventParameterType);
		// 创建一个新的事件订阅类
		SubscriptionObject newSubscriptionObject = new SubscriptionObject(
				subscriber, subscriberCallback, priority);
		// 无法通过回调方法的参数查找到事件订阅类，那么加入
		if (subscriptionObjects == null) {
			subscriptionObjects = new CopyOnWriteArrayList<SubscriptionObject>();
			// 以订阅对象的回调方法参数做键值，将回调对象加入缓存
			subscriptionObjectByEventParameterType.put(eventParameterType,
					subscriptionObjects);
		} else {
			for (SubscriptionObject subscriptionObject : subscriptionObjects) {
				// 判断订阅对象的回调方法是否已经注册过了
				if (subscriptionObject.equals(newSubscriptionObject)) {
					// 该回调方法已经注册过了
//					throw new IllegalArgumentException("Subscriber "
//							+ subscriber.getClass()
//							+ " already registered to event "
//							+ eventParameterType);
				}
			}
		}

		// 开始调整该参数类型的事件订阅类的优先级
		int size = subscriptionObjects.size();
		for (int i = 0; i <= size; i++) {
			// 重新调整优先级
			if (i == size
					|| newSubscriptionObject.priority > subscriptionObjects
							.get(i).priority) {
				// 优先级高的提前
				subscriptionObjects.add(i, newSubscriptionObject);
				break;
			}
		}

		// 根据订阅对象的类型，查找该对象的回调方法
		// 回去该订阅对象的所有回调方法列表引用
		List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
		if (subscribedEvents == null) {
			// 为订阅对象创建一个回调方法列表并加入到
			subscribedEvents = new ArrayList<Class<?>>();
			typesBySubscriber.put(subscriber, subscribedEvents);
		}
		// 将新的回调方法注册到该订阅对象的所有回调方法列表中
		subscribedEvents.add(eventParameterType);
	}

	/**
	 * Only updates subscriptionsByEventType, not typesBySubscriber! Caller must
	 * update typesBySubscriber.
	 */
	private void unubscribeByEventParameterType(Object subscriber,
			Class<?> eventParameterType) {
		// 根据参数类型，查找出所有订阅类
		List<SubscriptionObject> subscriptionObjects = subscriptionObjectByEventParameterType
				.get(eventParameterType);
		if (subscriptionObjects != null) {
			// 获取该参数类型的所有事件订阅对象
			int size = subscriptionObjects.size();
			for (int i = 0; i < size; i++) {
				// 遍历该参数类型的所有事件订阅对象
				SubscriptionObject subscriptionObject = subscriptionObjects
						.get(i);
				if (subscriptionObject.subscriber == subscriber) {
					// 仅仅移除指定的订阅类
					subscriptionObject.active = false;
					subscriptionObjects.remove(i);
					i--;
					size--;
				}
			}
		}
	}

	/**
	 * 开始向事件订阅对象抛出指定的event
	 * 
	 * @param event
	 */
	private void doPostALEvent(Object event) {
		// 把时间放到事件投递队列中
		PostingThreadState postingState = currentPostingThreadState.get();
		List<Object> eventQueue = postingState.eventQueue;
		eventQueue.add(event);

		if (postingState.isPosting) {
			// 正在投递，事件已经加入，那就慢慢等着被投递出去吧
			ALLog.i("正在投递");
			return;
		} else {
			postingState.isMainThread = Looper.getMainLooper() == Looper
					.myLooper();
			postingState.isPosting = true;
			if (postingState.canceled) {
				alEventDeliverWorkingStateCallback.onPostCancel();
				throw new IllegalArgumentException(
						"Internal error. Abort state was not reset");

			}
			Object waitingToPostEvent = null;
			try {
				// 把事件队列中的事件全部投递完
				while (!eventQueue.isEmpty()) {
					waitingToPostEvent = eventQueue.remove(0);
					postSingleEvent(waitingToPostEvent, postingState);
				}
			} finally {
				alEventDeliverWorkingStateCallback.onPostCompleted(waitingToPostEvent);
				postingState.isPosting = false;
				postingState.isMainThread = false;
				waitingToPostEvent = null;

			}
		}

	}

	private void postSingleEvent(Object event, PostingThreadState postingState)
			throws Error {
		// 获取投递的事件类
		Class<? extends Object> eventClass = event.getClass();
		// 获取该订阅对象的所有回调方法的参数
		List<Class<?>> eventTypes = findEventTypes(eventClass);
		boolean subscriptionFound = false;
		int countTypes = eventTypes.size();
		for (int h = 0; h < countTypes; h++) {
			Class<?> clazz = eventTypes.get(h);
			CopyOnWriteArrayList<SubscriptionObject> subscriptionObjects;
			synchronized (this) {
				// 根据抛出的事件，查找出订阅了该参数类型事件的订阅对象
				subscriptionObjects = subscriptionObjectByEventParameterType
						.get(clazz);
			}
			if (subscriptionObjects != null && !subscriptionObjects.isEmpty()) {
				for (SubscriptionObject subscriptionObject : subscriptionObjects) {
					postingState.event = event;
					postingState.subscription = subscriptionObject;
					boolean aborted = false;
					try {
						postToSubscriptionObject(subscriptionObject, event,
								postingState.isMainThread);
						aborted = postingState.canceled;
					} finally {
						postingState.event = null;
						postingState.subscription = null;
						postingState.canceled = false;
					}
					if (aborted) {
						break;
					}
				}
				subscriptionFound = true;
			}
		}
		if (!subscriptionFound) {
			ALLog.d("没有任何对象订阅了 " + eventClass + " 事件");
			// Log.d(TAG, "No subscribers registered for event " + eventClass);
			// if (eventClass != NoSubscriberEvent.class
			// && eventClass != SubscriberExceptionEvent.class) {
			// post(new NoSubscriberEvent(this, event));
			// }
		}
	}

	private void postToSubscriptionObject(
			SubscriptionObject subscriptionObject, Object event,
			boolean isMainThread) {
		switch (subscriptionObject.subscriberCallback.threadMode) {
		case PostThread:
			invokeSubscriber(subscriptionObject, event);
			break;
		case MainThread:
			if (isMainThread) {
				invokeSubscriber(subscriptionObject, event);
			} else {
				// mainThreadPoster.enqueue(subscriptionObject, event);
			}
			break;
		case BackgroundThread:
			if (isMainThread) {
				// backgroundPoster.enqueue(subscriptionObject, event);
			} else {
				invokeSubscriber(subscriptionObject, event);
			}
			break;
		case Async:
			// asyncPoster.enqueue(subscriptionObject, event);
			break;
		default:
			throw new IllegalStateException("Unknown thread mode: "
					+ subscriptionObject.subscriberCallback.threadMode);
		}
	}

	/** Finds all Class objects including super classes and interfaces. */
	/**
	 * 以抛出的事件为键，查找该对象以及父类以及实现接口的所有回调方法
	 * 
	 * @param eventClass
	 *            抛出的事件对象
	 * @return
	 */
	private List<Class<?>> findEventTypes(Class<?> eventClass) {
		synchronized (eventTypesCache) {
			List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
			if (eventTypes == null) {
				eventTypes = new ArrayList<Class<?>>();
				Class<?> clazz = eventClass;
				while (clazz != null) {
					eventTypes.add(clazz);
					addInterfaces(eventTypes, clazz.getInterfaces());
					clazz = clazz.getSuperclass();
				}
				eventTypesCache.put(eventClass, eventTypes);
			}
			return eventTypes;
		}
	}

	/** Recurses through super interfaces. */
	/**
	 * 递归查找所有接口
	 * 
	 * @param eventTypes
	 * @param interfaces
	 */
	static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
		for (Class<?> interfaceClass : interfaces) {
			if (!eventTypes.contains(interfaceClass)) {
				eventTypes.add(interfaceClass);
				addInterfaces(eventTypes, interfaceClass.getInterfaces());
			}
		}
	}
	
    void invokeSubscriber(PendingALEvent pendingALEvent) {
        Object event = pendingALEvent.event;
        SubscriptionObject subscription = pendingALEvent.subscriptionObject;
        PendingALEvent.releasePendingPost(pendingALEvent);
        if (subscription.active) {
            invokeSubscriber(subscription, event);
        }
    }

	void invokeSubscriber(SubscriptionObject subscription, Object event)
			throws Error {
		try {
			alEventDeliverWorkingStateCallback.onStartPostToSubscriber(subscription.subscriber, event);
			subscription.subscriberCallback.method.invoke(
					subscription.subscriber, event);
		} catch (InvocationTargetException e) {
//			e.toString();
			e.printStackTrace();
			ALLog.d("xxxxxxxxx "+e.toString()+" "+event.getClass().getSimpleName());
			/*
												 * Throwable cause =
												 * e.getCause(); if (event
												 * instanceof
												 * SubscriberExceptionEvent) {
												 * // Don't send another
												 * SubscriberExceptionEvent to
												 * avoid infinite event
												 * recursion, just log
												 * Log.e(TAG,
												 * "SubscriberExceptionEvent subscriber "
												 * +
												 * subscription.subscriber.getClass
												 * () + " threw an exception",
												 * cause);
												 * SubscriberExceptionEvent
												 * exEvent =
												 * (SubscriberExceptionEvent)
												 * event; Log.e(TAG,
												 * "Initial event " +
												 * exEvent.causingEvent +
												 * " caused exception in " +
												 * exEvent.causingSubscriber,
												 * exEvent.throwable); } else {
												 * if (logSubscriberExceptions)
												 * { Log.e(TAG,
												 * "Could not dispatch event: "
												 * + event.getClass() +
												 * " to subscribing class " +
												 * subscription
												 * .subscriber.getClass(),
												 * cause); }
												 * SubscriberExceptionEvent
												 * exEvent = new
												 * SubscriberExceptionEvent
												 * (this, cause, event,
												 * subscription.subscriber);
												 * postALEvent(exEvent); }
												 */
		} catch (IllegalAccessException e) {
			throw new IllegalStateException("Unexpected exception", e);
		}
	}

	
	
	private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
		@Override
		protected PostingThreadState initialValue() {
			return new PostingThreadState();
		}
	};

	/** For ThreadLocal, much faster to set (and get multiple values). */
	final static class PostingThreadState {
		List<Object> eventQueue = new ArrayList<Object>();
		boolean isPosting;
		boolean isMainThread;
		SubscriptionObject subscription;
		// 注册进来的订阅类
		Object event;
		boolean canceled;
	}
}
